/*--------------------------------------------------------------
m_axis_tuser define as below:
[32]---broadcast_flag
[31:16]---type
[15:0]---len
---------------------------------------------------------------*/

module mac_rx #(
    parameter           TARGET_MAC = 48'hff_ff_ff_ff_ff_ff      ,
    parameter           SOURCE_MAC = 48'h11_22_33_44_55_66      ,
    parameter           CRC_ON  = 1                             
)(
    input                       i_clk                           ,
    input                       i_rst                           ,
    input   [47: 0]             i_set_source_mac                ,
    input                       i_set_source_mac_valid          ,
    output                      m_axis_tvalid                   ,
    output  [7 : 0]             m_axis_tdata                    ,
    output                      m_axis_tlast                    ,
    output  [32: 0]             m_axis_tuser                    ,
    input   [7 : 0]             i_gmii_rxdata                   ,
    input                       i_gmii_rxvalid                   
);

    typedef struct packed {
        logic                   broadcast_flag                  ;
        logic   [15: 0]         packet_type                     ;
        logic   [15: 0]         data_len                        ;
        logic   [10: 0]         cached_start_addr               ;
    }s_mac_rx_info;

    logic   [47: 0]             set_source_mac                  ;
    logic                       gmii_rxvalid                    ;
    logic                       gmii_rxvalid_1d                 ;
    logic                       gmii_rxvalid_2d                 ;
    logic   [7 : 0]             gmii_rxdata                     ;
    logic   [15: 0]             rx_cnt                          ;
    logic   [3 : 0]             rx_head_cnt                     ;  
    logic   [47: 0]             rx_soruce_mac                   ;
    logic   [47: 0]             rx_target_mac                   ;
    logic                       crc_rst                         ;
    logic                       crc_valid                       ;
    logic   [31: 0]             rx_crc                          ;
    logic   [31: 0]             crc_result                      ;
    logic   [3 : 0][31: 0]      crc_result_d                    ;
    logic                       rx_crc_result_valid             ;
    logic                       rx_data_valid                   ;
    logic   [15: 0]             rx_type                         ;
    logic                       rx_is_broadcast                 ;
    logic                       rx_head_access                  ;
    logic                       mac_addr_access                 ;
    logic                       crc_access                      ;
    logic   [15: 0]             rx_data_len                     ;
    logic   [10: 0]             cur_cached_start_addr           ;
    s_mac_rx_info               rx_data_info                    ;
    logic   [10: 0]             bram_waddr                      ;
    logic                       bram_ren                        ;
    logic   [10: 0]             bram_raddr                      ;
    logic   [7 : 0]             bram_rddata                     ;
    logic   [15: 0]             read_len                        ;
    logic   [15: 0]             read_cnt                        ;
    logic   [10: 0]             read_start_addr                 ;
    logic                       fifo_wren                       ;
    logic                       fifo_rden                       ;
    logic                       fifo_rden_d                     ;
    logic                       fifo_full                       ;
    logic                       fifo_empty                      ;
    s_mac_rx_info               fifo_rddata                     ;
    logic   [32: 0]             axis_rx_user                    ;
    logic                       axis_rx_last                    ;
    logic                       axis_rx_valid                   ;   

    // ila128_bit ila128_bit_mac_rx(
    //     .clk    ( i_clk),
    //     .probe0 ( {gmii_rxvalid,gmii_rxvalid_1d,gmii_rxvalid_1d,gmii_rxdata,rx_cnt,crc_access,rx_crc,crc_result,rx_crc_result_valid})

    // );

    assign rx_data_info.packet_type = rx_type;
    assign rx_data_info.broadcast_flag = rx_is_broadcast;
    assign rx_data_info.data_len = rx_data_len;
    assign rx_data_info.cached_start_addr = cur_cached_start_addr;
    assign crc_rst = i_gmii_rxvalid & ~gmii_rxvalid;
    assign crc_valid = (rx_cnt>=8)?1:0;
    assign rx_crc_result_valid = ~gmii_rxvalid & gmii_rxvalid_1d;

    assign m_axis_tvalid = axis_rx_valid;  
    assign m_axis_tdata = bram_rddata;
    assign m_axis_tlast = axis_rx_last;
    assign m_axis_tuser = axis_rx_user;

    always_ff @( posedge i_clk ) begin
        if(i_rst)
            set_source_mac <= SOURCE_MAC;
        else if(i_set_source_mac_valid)
            set_source_mac <= i_set_source_mac;
        else  
            set_source_mac <= set_source_mac;
    end

    always_ff @(posedge i_clk) begin
        if(i_rst) begin
            gmii_rxvalid <= 0;
            gmii_rxdata <= 0;
        end
        else begin
            gmii_rxvalid <= i_gmii_rxvalid;
            gmii_rxdata <= i_gmii_rxdata;
        end
    end

    always_ff @(posedge i_clk) begin
        if(i_rst) begin
            gmii_rxvalid_1d <= 0;
            gmii_rxvalid_2d <= 0;
        end
        else begin
            gmii_rxvalid_1d <= gmii_rxvalid;
            gmii_rxvalid_2d <= gmii_rxvalid_1d;
        end
    end

    //Cache the current packet, output the current packet to the upper
    //layer if the CRC correctly and discard it if it is wrong.

    //ram is to cache the all data of packet
    sdpram_8x2048 sdpram_8x2048_inst0 (
        .clka                   ( i_clk                     ),
        .ena                    ( rx_data_valid             ),
        .wea                    ( rx_data_valid             ),
        .addra                  ( bram_waddr                ),
        .dina                   ( gmii_rxdata               ),
        .clkb                   ( i_clk                     ),
        .enb                    ( bram_ren                  ),
        .addrb                  ( bram_raddr                ),
        .doutb                  ( bram_rddata               ) 
    );

    //fifo is to cache the information of the packet
    sfifo_52x32 sfifo_52x32_inst0 (
        .clk                    ( i_clk                     ),      
        .din                    ( rx_data_info              ),     
        .wr_en                  ( fifo_wren                 ),  
        .rd_en                  ( fifo_rden                 ),  
        .dout                   ( fifo_rddata               ),    
        .full                   ( fifo_full                 ),    
        .empty                  ( fifo_empty                )  
    );

    CRC32_D8 CRC32_D8_u1(
        .data_in                ( gmii_rxdata               ),
        .crc_en                 ( crc_valid                 ),
        .crc_out                ( crc_result                ),
        .rst                    ( i_rst |crc_rst            ),
        .clk                    ( i_clk                     ) 
    );

    always_ff @( posedge i_clk ) begin
        if(i_rst)
            rx_cnt <= 0;
        else if(~i_gmii_rxvalid&gmii_rxvalid)
            rx_cnt <= 0;
        //Compatible with short heads(6 repeat 0x55),Ensure that data starts
        //at rx_cnt=8,regardless of whether the long or short head located.
        else if(rx_cnt==6 && rx_head_cnt==5 && gmii_rxdata == 8'hd5)
            rx_cnt <= rx_cnt + 2;
        else if(gmii_rxvalid)
            rx_cnt <= rx_cnt + 1;
        else  
            rx_cnt <= rx_cnt;
    end

    always_ff @( posedge i_clk ) begin
        if(i_rst)
            rx_head_cnt <= 0;
        else if(gmii_rxvalid == 0 && gmii_rxvalid_1d==1)
            rx_head_cnt <= 0;
        else if(gmii_rxvalid && rx_cnt<=6 && gmii_rxdata == 8'h55)
            rx_head_cnt <= rx_head_cnt + 1;
        else  
            rx_head_cnt <= rx_head_cnt;
    end

    always_ff @( posedge i_clk ) begin
        if(i_rst)
            rx_head_access <= 0;
        else if(i_gmii_rxvalid & !gmii_rxvalid)
            rx_head_access <= 0;
        //Compatible with short heads(6 repeat 0x55)
        else if(gmii_rxvalid && rx_head_cnt==6 && gmii_rxdata == 8'hd5)
            rx_head_access <= 1;
        else if(gmii_rxvalid && rx_head_cnt==7 && gmii_rxdata == 8'hd5)
            rx_head_access <= 1;
        else  
            rx_head_access <= rx_head_access;
    end

    always_ff @( posedge i_clk ) begin
        if(i_rst)
            rx_target_mac <= 0;
        else if(gmii_rxvalid && rx_cnt >= 8 && rx_cnt <= 13)
            rx_target_mac <= {rx_target_mac[39:0],gmii_rxdata};
        else  
            rx_target_mac <= rx_target_mac;
    end

    always_ff @( posedge i_clk ) begin
        if(i_rst)
            rx_soruce_mac <= 0;
        else if(gmii_rxvalid && rx_cnt >= 14 && rx_cnt <= 19)
            rx_soruce_mac <= {rx_soruce_mac[39:0],gmii_rxdata};
        else  
            rx_soruce_mac <= rx_soruce_mac;
    end

    always_ff @( posedge i_clk ) begin
        if(i_rst)
            rx_type <= 0;
        else if(gmii_rxvalid && rx_cnt >= 20 && rx_cnt <= 21)
            rx_type <= {rx_type[7:0],gmii_rxdata};
        else  
            rx_type <= rx_type;
    end

    always_ff @( posedge i_clk ) begin
        if(i_rst)
            rx_is_broadcast <= 0;
        //rx_target_mac is all 1 means that there is a broadcast packet
        else if(gmii_rxvalid && (rx_target_mac == 48'hff_ff_ff_ff_ff_ff))
            rx_is_broadcast <= 1;
        else if(gmii_rxvalid )
            rx_is_broadcast <= 0;
        else 
            rx_is_broadcast <= rx_is_broadcast;
    end

    always_ff @( posedge i_clk ) begin
        if(i_rst)
            mac_addr_access <= 0;
        else if(gmii_rxvalid && rx_target_mac == set_source_mac)
            mac_addr_access <= 1;
        else if(gmii_rxvalid )
            mac_addr_access <= 0;
        else  
            mac_addr_access <= mac_addr_access;
    end

    always_ff @( posedge i_clk ) begin
        if(i_rst)
            rx_crc <= 0;
        else if(gmii_rxvalid)
            rx_crc <= {rx_crc[23:0],gmii_rxdata};
        else 
            rx_crc <= rx_crc;
    end

    always_ff @( posedge i_clk ) begin
        if(i_rst)
            crc_result_d <= 0;
        else  
            crc_result_d <= {crc_result_d[2:0],crc_result};
    end

    always_ff @( posedge i_clk ) begin
        if(i_rst)
            crc_access <= 0;
        else if(rx_crc_result_valid && crc_result_d[3]=={rx_crc[7:0],rx_crc[15:8],rx_crc[23:16],rx_crc[31:24]})
            crc_access <= 1;
        else if(rx_crc_result_valid)
            crc_access <= 0;
        else  
            crc_access <= crc_access;
    end

    always_ff @( posedge i_clk ) begin
        if(i_rst)
            fifo_wren <= 0;
        else if(~gmii_rxvalid_1d && gmii_rxvalid_2d && rx_head_access && (mac_addr_access | rx_is_broadcast) && crc_access)
            fifo_wren <= 1;
        else 
            fifo_wren <= 0;
    end

    always_ff @( posedge i_clk ) begin
        if(i_rst)
            rx_data_len <= 0;
        else if(i_gmii_rxvalid && ~gmii_rxvalid)
            rx_data_len <= 0;
        else if(~i_gmii_rxvalid && gmii_rxvalid)
            rx_data_len <= rx_cnt - 25;
        else  
            rx_data_len <= rx_data_len;
    end

    always_ff @( posedge i_clk ) begin
        if(i_rst)
            rx_data_valid <= 0;
        else if(~i_gmii_rxvalid && gmii_rxvalid)
            rx_data_valid <= 0;
        else if(gmii_rxvalid && rx_cnt ==21)
            rx_data_valid <= 1;
        else 
            rx_data_valid <= rx_data_valid;
    end
    
    always_ff @( posedge i_clk ) begin
        if(i_rst)
            bram_waddr <= 0;
        else if(rx_data_valid && rx_cnt >= 8) 
            bram_waddr <= bram_waddr + 1;
        else  
            bram_waddr <= bram_waddr;
    end

    always_ff @( posedge i_clk ) begin
        if(i_rst)
            cur_cached_start_addr <= 0;
        else if(gmii_rxvalid && ~gmii_rxvalid_1d) 
            cur_cached_start_addr <= bram_waddr;
        else  
            cur_cached_start_addr <= cur_cached_start_addr;
    end
    
    always_ff @( posedge i_clk ) begin
        if(i_rst)
            fifo_rden <= 0;
        else if(!fifo_empty && !fifo_rden && !bram_ren && !fifo_rden_d) 
            fifo_rden <= 1;
        else  
            fifo_rden <= 0;
    end

    always_ff @( posedge i_clk ) begin
        if(i_rst)
            fifo_rden_d <= 0;
        else 
            fifo_rden_d <= fifo_rden;
    end
    
    always_ff @( posedge i_clk ) begin
        if(i_rst)
            read_len <= 0;
        else if(fifo_rden) 
            read_len <= fifo_rddata.data_len;
        else  
            read_len <= read_len;
    end

    always_ff @( posedge i_clk ) begin
        if(i_rst)
            read_start_addr <= 0;
        else if(fifo_rden) 
            read_start_addr <= fifo_rddata.cached_start_addr;
        else  
            read_start_addr <= read_start_addr;
    end


    
    
    always_ff @( posedge i_clk ) begin
        if(i_rst)
            bram_ren <= 0;
        else if(bram_ren && read_cnt >= read_len-1) 
            bram_ren <= 0;
        else if(fifo_rden_d ) 
            bram_ren <= 1;
        else  
            bram_ren <= bram_ren;
    end

    always_ff @( posedge i_clk ) begin
        if(i_rst)
            bram_raddr <= 0;
        else if(fifo_rden_d) 
            bram_raddr <= read_start_addr;
        else if(bram_ren) 
            bram_raddr <= bram_raddr + 1;
        else  
            bram_raddr <= bram_raddr;
    end

    always_ff @( posedge i_clk ) begin
        if(i_rst)
            read_cnt <= 0;
        else if(read_cnt >= read_len-1) 
            read_cnt <= 0;
        else if(bram_ren) 
            read_cnt <= read_cnt + 1;
        else  
            read_cnt <= read_cnt;
    end

    always_ff @( posedge i_clk ) begin
        if(i_rst)
            axis_rx_valid <= 0; 
        else  
            axis_rx_valid <= bram_ren;
    end

    always_ff @( posedge i_clk ) begin
        if(i_rst)
            axis_rx_last <= 0;
        else if(read_cnt >= read_len-1) 
            axis_rx_last <= 1;
        else  
            axis_rx_last <= 0;
    end

    always_ff @( posedge i_clk ) begin
        if(i_rst)
            axis_rx_user <= 0; 
        else  if(fifo_rden)
            axis_rx_user <= {fifo_rddata.broadcast_flag,fifo_rddata.packet_type,fifo_rddata.data_len};
        else  
            axis_rx_user <= axis_rx_user;
    end




   
endmodule
